package com.mezz.bones.framework.web.aspect;

import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.DigestUtil;
import com.alibaba.fastjson.JSONObject;
import com.mezz.bones.framework.base.domain.enums.ResponseCode;
import com.mezz.bones.framework.base.exception.BaseException;
import com.mezz.bones.framework.core.annotation.RepeatSubmit;
import com.mezz.bones.framework.web.context.LoginUserContext;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeUnit;

/**
 * 防重复提交 AOP 实现
 */
@Aspect
@Component
@Slf4j
public class RepeatSubmitAspect {

    @Resource
    private RedisTemplate<String, String> redisTemplate;

    @Resource
    private HttpServletRequest request; // 用于获取当前请求路径

    private final SpelExpressionParser spelParser = new SpelExpressionParser();

    @Around("@annotation(com.mezz.bones.framework.core.annotation.RepeatSubmit)")
    public Object interceptor(ProceedingJoinPoint pjp) throws Throwable {
        // 获取注解
        MethodSignature signature = (MethodSignature) pjp.getSignature();
        RepeatSubmit lockAnnotation = signature.getMethod().getAnnotation(RepeatSubmit.class);

        // 获取用户ID，如果未登录则为空字符串
        String userId = LoginUserContext.getCurrentUserIdOrEmpty();

        // 获取当前请求路径
        String requestPath = request.getRequestURI();

        // 生成Redis的key
        String repeatSubmitKey = generateRepeatSubmitKey(userId, requestPath, lockAnnotation, pjp);

        // 获取Redis操作对象
        ValueOperations<String, String> ops = redisTemplate.opsForValue();

        // 尝试设置key，避免重复提交
        boolean isFirstSubmit = ops.setIfAbsent(repeatSubmitKey, "1", lockAnnotation.value(), TimeUnit.SECONDS);

        if (!isFirstSubmit) {
            log.warn("重复提交 detected, userId: {}, requestPath: {}", userId, requestPath);
            throw new BaseException(ResponseCode.REPEAT_SUBMIT);
        }

        return pjp.proceed();  // 执行目标方法

    }

    /**
     * 生成Redis的key，基于userId、请求路径、自定义请求ID以及业务参数
     */
    private String generateRepeatSubmitKey(String userId, String requestPath, RepeatSubmit lockAnnotation, ProceedingJoinPoint pjp) {
        StringBuilder sb = new StringBuilder();

        // 用户ID（如果未登录，则为空字符串）
        if (StrUtil.isNotBlank(userId)) {
            sb.append(userId).append(":");
        } else {
            sb.append("anno").append(":");
        }

        // 请求路径
        sb.append(requestPath);

        buildOtherInfo(sb,lockAnnotation, pjp);

        // 使用MD5生成锁的键
        return DigestUtil.md5Hex(sb.toString().getBytes(StandardCharsets.UTF_8));
    }

    private void buildOtherInfo(StringBuilder sb, RepeatSubmit lockAnnotation, ProceedingJoinPoint pjp) {

        RepeatSubmit.RequestIdentifierStrategy requestIdentifierStrategy = lockAnnotation.uniqueRequestIdentifier();


        if(requestIdentifierStrategy == RepeatSubmit.RequestIdentifierStrategy.PATH_ONLY) {
            return;
        }

        if (requestIdentifierStrategy == RepeatSubmit.RequestIdentifierStrategy.PATH_AND_PARAMS) {
            Object[] args = pjp.getArgs();
            if (args != null) {
                for (Object arg : args) {
                    if (arg != null) {

                        // 将对象序列化为JSON字符串
                        String jsonArg = JSONObject.toJSONString(arg);
                        sb.append(":").append(jsonArg);
                    }
                }
            }
            return;
        }


        if (requestIdentifierStrategy == RepeatSubmit.RequestIdentifierStrategy.CUSTOM) {
            String customRequestId = parseSpelExpression(lockAnnotation.customKey(), pjp);
            sb.append(":").append(customRequestId);
            return;
        }
    }

    /**
     * 将业务参数添加到锁键
     */
    private void appendBusinessParams(StringBuilder sb, Object[] args) {

        if (args != null) {

            for (Object arg : args) {
                if (arg != null) {

                    // 将对象序列化为JSON字符串
                    String jsonArg = JSONObject.toJSONString(arg);
                    sb.append(":").append(jsonArg);
                }
            }
        }
    }

    /**
     * 解析SpEL表达式，获取自定义请求ID
     */
    private String parseSpelExpression(String spelExpression, ProceedingJoinPoint pjp) {
        if (StrUtil.isBlank(spelExpression)) {
            return "";
        }

        // 创建SpEL上下文
        StandardEvaluationContext context = new StandardEvaluationContext();
        MethodSignature signature = (MethodSignature) pjp.getSignature();
        Object[] args = pjp.getArgs();
        String[] parameterNames = signature.getParameterNames();

        // 将方法参数放入SpEL上下文中
        for (int i = 0; i < parameterNames.length; i++) {
            context.setVariable(parameterNames[i], args[i]);
        }
        // 解析SpEL表达式
        try {
            Expression expression = spelParser.parseExpression(spelExpression);
            return expression.getValue(context, String.class);
        } catch (Exception e) {
            log.error("SpEL 表达式解析失败: {}", spelExpression, e);
            throw new BaseException(ResponseCode.SPEL_EXPRESSION_ERROR);
        }
    }
}
